///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003-5 Jason Baldridge and University of Edinburgh (Michael White)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//////////////////////////////////////////////////////////////////////////////

package opennlp.ccg.synsem;

import opennlp.ccg.parse.*;
import opennlp.ccg.parsecat.*;
import opennlp.ccg.unify.UnifyControl;
import opennlp.ccg.util.*;
import opennlp.ccg.lexicon.*;
import opennlp.ccg.grammar.*;
import opennlp.ccg.hylo.*;

import org.jdom.*;
import gnu.trove.*;
import java.util.*;

import java.io.*;

/**
 * A CCG sign, consisting of a list of words paired with a category.
 *
 * @author      Jason Baldridge
 * @author      Michael White
 * @version     $Revision: 1.25 $, $Date: 2005/10/18 22:20:15 $
 */
public class Sign implements Comparable {
    
    /** The words. */
    protected List<Word> _words;
    
    /** The category. */
    protected Category _cat;
    
    /** The derivation history. */
    protected DerivationHistory _history;
    
    // Added D.N. Mehy (19 Nov. 2007) -- needed for dep. graph construction.
    protected Collection<Word> _headWords;
    protected Collection<Word> _depWords;
    
    // The dependency structure.
    protected List<SynDependency> _dependencies;
    
    /** Interner for categories. */
    protected static Interner<Category> catInterner = null;
    
    /** (Added by D.N. Mehay, sometime in November 2007).
     * The probability of this sign (given whatever relevant concerns).
     * Defaults to 0.0; to be overwritten by a prob.-sensitive subclass
     * constructor.
     */
    protected double logProb = 0.0;
    
    /** Added D.N. Mehay (20 Nov 2007): This tells us whether this Sign has been type-raised.
     *  If so, it cannot be a "head" -- this is just a hack to get at some sort of head-dep
     *  relations for head-dep reranking.
     **/
    protected boolean _hasBeenTypeRaised = false;
    
    //Added D.N. Mehay (21 Nov 2007)
    /** Keeps track of canonical position args that have been filled.
     * A value of false at index i means that arg slot i+1 has not been
     * filled yet.  Take care to double-check with the arity of the
     * complex cat to see whether it does indeed have 4 args.
     */
    public boolean[] _argsFilled;
    
    /** Interns the given category, if cat interning enabled, otherwise just returns it. */
    public static Category internCat(Category cat) {
        return (catInterner != null) ? catInterner.intern(cat) : cat;
    }
    
    /** Resets and enables/disables cat interning according to the given flag.
     * Interning categories speeds up the search during realization by using a
     * canonical category for equivalent ones, thereby speeding up hashing
     * and reducing memory demands.
     * The interner must be reset for each realization request, to avoid
     * problems with UnifyControl's sequencing.
     * Cat interning should be disabled for parsing (at present), since
     * there are no constants to distinguish categories for two instances of
     * the same lexical item, unlike in realization. */
    public static void resetCatInterner(boolean on) { catInterner = (on) ? new Interner<Category>() : null; }
    
    
    /** Constructor for subclasses. */
    protected Sign() {}
    
    /** Constructor with derivation history. */
    @SuppressWarnings("unchecked")
    protected Sign(List<Word> words, Category cat, DerivationHistory dh) {
        _words = (List) Interner.globalIntern(words);
        _cat = internCat(cat);
        _history = dh;
        //int headArity;
        //if(_headWords != null) {
        //    headArity = inferArity(_headWords.iterator().next().getSupertag());
        //} else {
        //    headArity = inferArity(words.get(words.size()-1).getSupertag());
        //}
        //_argsFilled = new boolean[headArity];
    }
    
    // D.N. Mehay (21 Nov 2007).
    /**
     * Infer the arity of a Category by the number of slashes in its
     * supertag.
     */
    /*private static int inferArity(String supertag) {
        char[] st = supertag.toCharArray();
        int slashCount = 0;
        for(int i = 0; i < st.length; i++) {
            if(st[i]=='/' || st[i]=='\\'){
                slashCount++;
            }
        }
        return slashCount;
				}*/

		//Added by Raja(Dec 3)
		//Function which infers arity of a supertag
		private static int inferArity(String supertag) {


				int arity=0;
        
				if(supertag.contains("/") || supertag.contains("\\")){
				    
				    try {
					
					//Parses a CCGbank complex category and returns a parse tree. 
					CCGbankCategory ccg=new CCGbankCategory(new StringReader(supertag));
					//Tree walking stub. For later use
					WalkTree wkt=new WalkTree();
					
					SimpleNode root=ccg.start();
					//wkt.eval(root);
					//Go 2 levels down to get actual root. 
					SimpleNode leaf1=(SimpleNode)root.jjtGetChild(0);
					SimpleNode leaf2=(SimpleNode)leaf1.jjtGetChild(0);
					//System.out.println("CAT:" +supertag);
					//wkt.rootEval(leaf2);
					//Get children at root level
					int numC=leaf2.jjtGetNumChildren();
					
					//Arity calc
					double temp = numC/2.0;
					arity=(int)Math.ceil(temp)-1;
					
					//System.out.println("Arity: "+numC+" "+temp+" "+arity);
					//System.out.println('\n');
					
					
				    }
				    
				    catch (Exception exc) {
					System.out.println(exc.toString());
				    }
				    
				}
				else arity =0;
				
				return arity;
				
		}

    
    /** Constructor with no additional derivation history. */
    public Sign(List<Word> words, Category cat) {
        this(words, cat, null);
        _history = new DerivationHistory(this);
    }
    
    /** Constructor with no additional derivation history. */
    public Sign(Word word, Category cat) {
        this(new SingletonList<Word>(word), cat);
    }
    
    /** Factory method for creating a sign from a lexical sign plus a coarticulation one. */
    public static Sign createCoartSign(Category cat, Sign lexSign, Sign coartSign) {
        System.out.println("1");
				

        List words = lexSign.getWords();
        if (words.size() > 1)
            throw new RuntimeException("Can't create coarticulation sign from multiple words.");
        Word word = (Word) words.get(0);
        Word coartWord = (Word) coartSign.getWords().get(0);
        Word wordPlus = Word.createWordWithAttrs(word, coartWord);
        Sign retval = new Sign(new SingletonList<Word>(wordPlus), cat, null);
        Rule coartRule = new Rule() {
            public String name() { return "coart"; }
            public int arity() { return 1; }
            public List<Category> applyRule(Category[] inputs) { throw new RuntimeException("Not supported."); }
            public RuleGroup getRuleGroup() { throw new RuntimeException("Not supported."); }
            public void setRuleGroup(RuleGroup ruleGroup) { throw new RuntimeException("Not supported."); }
        };
        retval._history = new DerivationHistory(new Sign[]{lexSign,coartSign}, retval, coartRule);
        return retval;
    }
    
    /** Factory method for creating derived signs with the given cat from the given inputs and rule. */
    public static Sign createDerivedSign(Category cat, Sign[] inputs, Rule rule) {
        
        Collection<Word> headWds = null,
	    depWds = null;
        
	//createDerivedSignWithNewLF(cat,inputs,rule);


        // The dependencies to be percolated up.
        List<SynDependency> deps = null, deps2Temp = null;
        // Dumb head-finding rules added by Raja and Dennis (19 Nov 2007)
        // Replace them with something sensible...if you dare!
        
        int indexOfHead = -1, indexOfDependent = -1;
        //boolean[] marksOfArgsFilled = null;
				//System.out.println(rule.name() );

				//System.out.println(cat.getLF()+" "+"Raja");
				//System.out.println('\n');

        if (rule instanceof TypeChangingRule) {
            indexOfHead = 0;
        } else if(rule instanceof ForwardTypeRaising) {
            indexOfHead = 0;
        } else if(rule instanceof BackwardTypeRaising) {
            indexOfHead = 0;
        } else if(rule instanceof ForwardApplication) {
            if(!inputs[0].hasBeenTypeRaised()) {
                indexOfHead = 0; indexOfDependent = 1;
            } else {
                indexOfHead = 1; indexOfDependent = 0;
            }
        } else if(rule instanceof BackwardApplication) {
            if(!inputs[1].hasBeenTypeRaised()) {
                indexOfHead = 1; indexOfDependent = 0;
            } else {
                indexOfHead = 0; indexOfDependent = 1;
            }
        } else if(rule instanceof ForwardComposition) {
            if(!inputs[0].hasBeenTypeRaised()) {
                indexOfHead = 1; indexOfDependent = 0;
                // Plus, put some stuff on the stack ... TBD
            } else {
                indexOfHead = 0; indexOfDependent = 1;
            }
        } else if(rule instanceof BackwardComposition) {
            if(!inputs[1].hasBeenTypeRaised()) {
                indexOfHead = 1; indexOfDependent = 0;
                // Plus, put some stuff on the stack ... TBD
            } else {
                indexOfHead = 0; indexOfDependent = 1;
            }
        } else if(rule instanceof ForwardSubstitution) {
            indexOfHead = 0; indexOfDependent = 1;
        }
        
        headWds = inputs[indexOfHead].getHeadWords();
        //marksOfArgsFilled = inputs[indexOfHead]._argsFilled;
        //int argNumFilled = -1;
        
        
        // Get the dependencies from component Sign's, if any.
        deps = inputs[indexOfHead].getDependencies();
        if(indexOfDependent > -1)
            deps2Temp = inputs[indexOfDependent].getDependencies();
        if(deps!=null) {
            if(deps2Temp!=null) {
                deps.addAll(deps2Temp);
            }
        } else {
            deps = deps2Temp;
        }
        
        // -------------- INFER HEAD WORDS IF LEXICAL CATEGORY -------
        // (Really easy; just pick the only word.)
        //
        //  This is the case where the input at 0 is a lexical
        // category (pre-terminal).
        if(headWds == null && inputs[indexOfHead]._words.size()==1) {
            headWds = new ArrayList<Word>();
            headWds.add(inputs[indexOfHead]._words.get(0));
            inputs[0].setHeadWords(headWds);
        }
        // Again, the dependent was a pre-terminal.
        if(depWds == null && inputs.length > 1) {
            if(inputs[indexOfDependent]._words.size() == 1) {
                depWds = new ArrayList<Word>();
                depWds.add(inputs[indexOfDependent]._words.get(0));
                
            }
        }
        
        
        // ------------------ GET DEP SLOT FILLED --------------------
        //int headArity = inferArity(inputs[indexOfHead].getHeadWords().iterator().next().getSupertag());
        //if(indexOfDependent > -1) {
        //    depWds = inputs[indexOfDependent].getHeadWords();
        //}
        //
        //if(depWds != null) {
        //    System.out.println("arity: "+headArity);
        //    System.out.println("length of marksofargs...: "+marksOfArgsFilled.length);
        //    if(indexOfHead == 0) {
        //        // look from right to left for an unfilled arg.
        //        for(int qp = headArity-1; qp >= 0; qp--) {
        //            System.out.println("qp: "+qp);
        //            if(!marksOfArgsFilled[qp]) {
        //                marksOfArgsFilled[qp] = true;
        //                argNumFilled = qp + 1;
        //            }
        //        }
        //    } else {
        //        // look from left to right for an unfilled arg.
        //
        //        for(int qp = 0; qp < headArity; qp++) {
        //            if(!marksOfArgsFilled[qp]) {
        //                marksOfArgsFilled[qp] = true;
        //                argNumFilled = qp + 1;
        //            }
        //        }
        //    }
        //}
        
        Sign retVal = new Sign(cat, inputs, rule);
        
        retVal.setHeadWords(headWds);
        
        if(depWds != null) {
            retVal.setDepWords(depWds);
        }
        
        // Build dep structures for passing on.
        // (Remember that the "head" could be a conjunction
        // of head words (same for the "dependent"), hence the
        // possibility of multiple head and dependent words.
        //
        if(deps == null) {
            deps = new ArrayList<SynDependency>(20);
        }
        SynDependency tempD = null;
        String direction;
        if((headWds != null) && (depWds != null)) {
            for(Word head : headWds) {
                for(Word dep : depWds) {
                    // Add word-word, POS-word, word-POS and POS-POS dependencies.
                    
                    // Is the dependent to the right or left of the head?
                    if(indexOfHead < indexOfDependent)
                        direction = "R";
                    else {
                        direction = "L";
                    }
                    deps.add(new SynDependency(head.getForm(), dep.getForm(), head.getSupertag(), dep.getSupertag(), direction));
                    deps.add(new SynDependency(head.getPOS(), dep.getForm(), head.getSupertag(), dep.getSupertag(), direction));
                    deps.add(new SynDependency(head.getForm(), dep.getPOS(), head.getSupertag(), dep.getSupertag(), direction));
                    deps.add(new SynDependency(head.getPOS(), dep.getPOS(), head.getSupertag(), dep.getSupertag(), direction));
                }
            }
        }
        if(deps.size() > 0)
            retVal.setDependencies(deps);
        //for(SynDependency dep : deps)
        //    System.out.println(dep);
        // Pass on other values of interest.        
        //System.out.println("head word: "+inputs[indexOfHead].getHeadWords().iterator().next());        
        return retVal;
    }
    
    /** Factory method for creating derived signs from the given result cat, inputs and rule,
     * with a new LF constructed from the inputs.
     * Note that unlike with rule applications, the result LF is constructed with
     * no var substitutions, so it is useful only for creating alternative signs during realization. */
    public static Sign createDerivedSignWithNewLF(Category cat, Sign[] inputs, Rule rule) {
        Category copyCat = cat.shallowCopy();
        LF lf = null;

				String lf1="";
				String lf2="";
				

        for (int i = 0; i < inputs.length; i++) {
            lf = HyloHelper.append(lf, inputs[i].getCategory().getLF());
						
						if(i==0) lf1=Integer.toString(i)+" "+inputs[i].getCategory().getSupertag()+" "+inputs[i].getCategory().getLF();

						else lf2=Integer.toString(i)+" "+inputs[i].getCategory().getSupertag()+" "+inputs[i].getCategory().getLF();
						inferArity(inputs[i].getCategory().getSupertag());
						

        }

        /*System.out.println(lf1);
				System.out.println(lf2);
				System.out.println(rule.name());
				System.out.println('\n');*/
				
        if (rule instanceof TypeChangingRule) {
            TypeChangingRule tcr = (TypeChangingRule) rule;
            lf = HyloHelper.append(lf, tcr.getResult().getLF());
            // For unary rules, just percolate up the dependency graph.
        }
        
        if (lf != null) { HyloHelper.sort(lf); }
        
        copyCat.setLF(lf);
        Category resultCat = internCat(copyCat);
        return new Sign(resultCat, inputs, rule);
    }
    
    /** Constructor with words and derivation history formed from the given inputs and rule. */
    protected Sign(Category cat, Sign[] inputs, Rule rule) {
        this(getRemainingWords(inputs, 0), cat, null);
        _history = new DerivationHistory(inputs, this, rule);
    }
    
    /**
     * Added by D.N. Mehay (04 Nov 2007): A getter for the (potentially default and meaningless)
     * log-probability of this sign.
     * @return The log-probability of this sign as a <code>double</code>.
     */
    public double getLogProb() { return this.logProb; }
    
    /**
     * Added by D.N. Mehay (04 Nov 2007): A setter for the (potentially default and meaningless)
     * log-probability of this sign.
     * @param newLogProb The new log-prob value (a <code>double</code>).
     */
    public void setLogProb(double newLogProb) { this.logProb = newLogProb; }
    
    // returns the remaining words in a structure sharing way
    protected static List<Word> getRemainingWords(Sign[] inputs, int index) {
        // if (inputs.length == 0) throw new RuntimeException("Error: can't make sign from zero inputs");
        if (index == (inputs.length - 1)) return inputs[index]._words;
        return new StructureSharingList<Word>(
                inputs[index]._words,
                getRemainingWords(inputs, index+1)
                );
    }
    
    /** (D.N. Mehay) Marks this Sign as having been type-raised. */
    public void markAsTypeRaised() {
        _hasBeenTypeRaised = true;
    }
    
    /** (D.N. Mehay) query to see whether this Sign has been type-raised. */
    public boolean hasBeenTypeRaised() {
        return _hasBeenTypeRaised;
    }
    /** (D.N. Mehay) Returns the dependency graph of this Sign */
    public List<SynDependency> getDependencies() {
        return _dependencies;
    }
    
    /** Sets the dependency graph of this Sign. */
    public void setDependencies(List<SynDependency> newDeps) {
        _dependencies = newDeps;
    }
    
    /** Returns the collection of head words (usually of cardinality 1) of the sign. (D.N. Mehay)*/
    public Collection<Word> getHeadWords() {
        return _headWords;
    }
    
    /** Returns the set of dependent words (usually of cardinality 1) of the sign. (D.N. Mehay)*/
    public Collection<Word> getDepWords() {
        return _depWords;
    }
    
    /** Sets the head words set of the sign (D.N. Mehay)*/
    public void setHeadWords(Collection<Word> newHeads) {
        _headWords = newHeads;
    }
    
    /** Set the dependent word set of the sign (D.N. Mehay)*/
    public void setDepWords(Collection<Word> newDeps) {
        _depWords = newDeps;
    }
    
    /** Returns the words of the sign. */
    public List<Word> getWords() {
        return _words;
    }
    
    /** Returns the words as a string.  Delegates to the current tokenizer's getOrthography method. */
    public String getOrthography() {
        return Grammar.theGrammar.lexicon.tokenizer.getOrthography(_words);
    }
    
    /** Returns the sign's category. */
    public Category getCategory() {
        return _cat;
    }
    
    /** Sets the derivation history. */
    public void setDerivationHistory(DerivationHistory dh) {
        _history = dh;
    }
    
    /** Returns the derivation history. */
    public DerivationHistory getDerivationHistory() {
        return _history;
    }
    
    
    /** Returns a hash code for this sign. */
    public int hashCode() {
        // Added by D.N. Mehay (04 Nov 2007): the hashCode() function now incorporates
        // the String form of the logProb.
        return System.identityHashCode(_words) + _cat.hashCode() + (this.logProb+"").hashCode();
        // +
        //        Arrays.hashCode(_argsFilled);
    }
    
    /** Returns whether this sign equals the given object. */
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof Sign)) return false;
        Sign sign = (Sign) obj;
        // Added by D.N. Mehay (04 Nov 2007): Made equals consistent with new hash
        // function (see above).
        return _words == sign._words && _cat.equals(sign._cat) &&
                this.logProb == sign.getLogProb();
    }
    
    
    /** Returns a hash code for this sign with the words restricted to surface words. */
    public int surfaceWordHashCode() {
        int hc = 1;
        for (int i = 0; i < _words.size(); i++) {
            Word word = (Word) _words.get(i);
            hc = 31*hc + word.surfaceWordHashCode();
        }
        hc += _cat.hashCode();
        return hc;
    }
    
    /** Returns whether this sign and the given object have equal categories and
     * restrictions to surface words. */
    public boolean surfaceWordEquals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof Sign)) return false;
        Sign sign = (Sign) obj;
        if (_words.size() != sign._words.size()) return false;
        for (int i = 0; i < _words.size(); i++) {
            Word word = (Word) _words.get(i);
            Word signWord = (Word) sign._words.get(i);
            if (!word.surfaceWordEquals(signWord)) return false;
        }
        return _cat.equals(sign._cat);
    }
    
    
    /** Returns 'orthography :- category'. */
    public String toString() {
        return getOrthography() + " :- " + _cat.toString();
    }
    
    
    /**
     * Returns the words in an XML doc, with no labeled spans for nominals.
     */
    public Document getWordsInXml() { return getWordsInXml(Collections.EMPTY_SET); }
    
    /**
     * Returns the words in an XML doc, with labeled spans for the given nominals,
     * and with pitch accents and boundary tones converted to elements.
     * Each orthographic word appears in a separate element,
     * with multiwords grouped under a multiword element.
     * Attribute-value pairs for the word (if any) appear on the word
     * or multiword element.
     * Words are also expanded using the grammar's tokenizer.
     */
    public Document getWordsInXml(Set nominals) {
        TObjectIntHashMap nominalsMap = new TObjectIntHashMap();
        setMaxOrthLengths(nominals, nominalsMap);
        Document doc = new Document();
        Element root = new Element("seg");
        doc.setRootElement(root);
        addWordsToXml(root, nominalsMap);
        return doc;
    }
    
    // finds the maximum orthography lengths for signs headed by the given nominals
    private void setMaxOrthLengths(Set nominals, TObjectIntHashMap nominalsMap) {
        // update map
        Nominal index = _cat.getIndexNominal();
        if (index != null && nominals.contains(index)) {
            int orthLen = getOrthography().length();
            if (!nominalsMap.containsKey(index) || orthLen > nominalsMap.get(index)) {
                nominalsMap.put(index, orthLen);
            }
        }
        // recurse
        Sign[] inputs = _history.getInputs();
        if (inputs == null) return;
        for (int i = 0; i < inputs.length; i++) {
            inputs[i].setMaxOrthLengths(nominals, nominalsMap);
        }
    }
    
    // recursively adds orthographic words as XML to the given parent,
    // using the nominals map to determine labeled spans
    private void addWordsToXml(Element parent, TObjectIntHashMap nominalsMap) {
        // check for matching nominal as index of target cat;
        // if found, update parent to labeled span element
        Nominal index = _cat.getIndexNominal();
        if (index != null && nominalsMap.containsKey(index) &&
                nominalsMap.get(index) == getOrthography().length()) {
            // remove index key from map, to avoid duplicate spans with the same length
            nominalsMap.remove(index);
            // make span element, update parent
            Element span = new Element("span");
            span.setAttribute("label", index.toString());
            parent.addContent(span);
            parent = span;
        }
        // process inputs from derivation history
        Sign[] inputs = _history.getInputs();
        if (inputs == null) {
            // in leaf case, word list must be a singleton
            Word word = (Word) _words.get(0);
            // check for boundary tone
            if (Grammar.isBoundaryTone(word.getForm())) {
                // add element for boundary tone
                Element boundary = new Element("boundary");
                boundary.setAttribute("type", word.getForm());
                parent.addContent(boundary);
                return;
            }
            // check for pitch accent
            if (word.getPitchAccent() != null) {
                // add pitchaccent element containing word(s) with corresponding accent
                Element pitchaccent = new Element("pitchaccent");
                pitchaccent.setAttribute("type", word.getPitchAccent());
                addWords(pitchaccent, word);
                parent.addContent(pitchaccent);
                return;
            }
            // otherwise add word(s)
            addWords(parent, word);
            return;
        }
        if (inputs.length == 1) {
            inputs[0].addWordsToXml(parent, nominalsMap);
            return;
        }
        for (int i = 0; i < inputs.length; i++) {
            inputs[i].addWordsToXml(parent, nominalsMap);
        }
    }
    
    // adds one or more word elements after expanding surface form;
    // multiwords are enclosed within a multiword element;
    // any attribute-value pairs are added to the word or multiword element
    private void addWords(Element parent, Word word) {
        List orthWords = Grammar.theGrammar.lexicon.tokenizer.expandWord(word);
        Element child;
        if (orthWords.size() == 1) {
            Element wordElt = new Element("word");
            wordElt.addContent((String)orthWords.get(0));
            child = wordElt;
        } else {
            Element multiwordElt = new Element("multiword");
            for (int i = 0; i < orthWords.size(); i++) {
                Element wordElt = new Element("word");
                wordElt.addContent((String)orthWords.get(i));
                multiwordElt.addContent(wordElt);
            }
            child = multiwordElt;
        }
        for (Iterator it = word.getAttrValPairs(); it.hasNext(); ) {
            Pair p = (Pair) it.next();
            String attr = (String) p.a; String val = (String) p.b;
            child.setAttribute(attr, val);
        }
        parent.addContent(child);
    }
    
    
    /**
     * Returns a string showing the bracketings implied by the derivation.
     * See DerivationHistory.toString to see the complete derivation in
     * vertical list form.
     */
    public String getBracketedString() {
        Sign[] inputs = _history.getInputs();
        if (inputs == null) return getOrthography();
        if (inputs.length == 1) return inputs[0].getBracketedString();
        StringBuffer sb = new StringBuffer();
        sb.append("(");
        for (int i = 0; i < inputs.length; i++) {
            sb.append(inputs[i].getBracketedString());
            if (i < (inputs.length - 1)) sb.append(" ");
        }
        sb.append(")");
        return sb.toString();
    }
    
    /**
     * Implements the Comparable interface (for sorting).
     * @param arg0 An <code>Object</code> for comparison.
     * @return 0 if this Sign and arg0 are equal, -1 if arg0 is "less than"
     *         this Sign and 1 if arg0 is "greater than" this Sign.
     */
    public int compareTo(Object arg0) throws ClassCastException {
        double compValArg0 = ((Sign)arg0).getLogProb();
        return ((int)(Math.signum(compValArg0-this.getLogProb())));
    }
}
